package patterns;

import java.util.ArrayList;
import java.util.List;

/**
 * 单例模式
 */
public class LearnSingleton {
    public static void main(String[] args) {
        Singleton_hungry singletonHungry = Singleton_hungry.getInstance();
        Singleton_lazy singletonLazy = Singleton_lazy.getInstance();
        Singleton_syn singletonSyn = Singleton_syn.getInstance();
    }
}

// 饿汉模式
final class Singleton_hungry {
    // 自行创建实例
    private static Singleton_hungry instance = new Singleton_hungry();

    // 构造函数
    private Singleton_hungry() {
        System.out.println("Singleton1对象被实例化了");
    }

    // 通过该函数向整个系统提供实例
    public static Singleton_hungry getInstance() {
        return instance;
    }
}

// 懒汉模式
final class Singleton_lazy {
    // 不实例化
    private static Singleton_lazy instance = null;

    // 构造函数
    private Singleton_lazy() {
        System.out.println("Singleton2对象被实例化了");
    }

    // 通过该函数向整个系统提供实例
    public static Singleton_lazy getInstance() {
        // 当instance为null时，则实例化对象，否则直接返回对象
        if (null == instance) {
            // 实例化对象
            instance = new Singleton_lazy();
        }
        // 返回已存在的对象
        return instance;
    }
}

// 懒汉模式 + 同步锁 + double-check = 双锁检查单例
final class Singleton_syn {
    // 解释加上volatile的原因↓
    // 示例过程:
    // 1. 分配instance对象的内存
    // 2. 调用构造函数,初始化成员变量
    // 3. 将instance指向分配的内存空间(执行完这个,instance为非null)
    // 若虚拟机发生了重排优化,先执行了3,还没执行2,此时另一个线程来获取instance,发现为非null,就拿到了,但拿到的是空值
    // volatile保证线程间变量的可见性(线程A对变量修改后,之后的线程B能看到改动),也阻止局部重排序的发生
    private static volatile Singleton_syn instance = null;

    public List<String> list = null;

    // 构造函数
    private Singleton_syn() {
        list = new ArrayList<>();
        System.out.println("Singleton_syn对象被实例化了");
    }

    // 通过该函数向整个系统提供实例,加同步锁
    public static Singleton_syn getInstance() {
        // 当instance为null时，则实例化对象，否则直接返回对象
        // DoubleCheck 提高多线程下懒汉模式的性能
        if (null == instance) {
            // 实例化对象
           synchronized (Singleton_syn.class) {
               // 在锁里面double-check,因为可能有多个线程进入到第一个if里,把instance重新创建
               if (null == instance)
                   instance = new Singleton_syn();
           }
        }
        // 返回已存在的对象
        return instance;
    }
}
